﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using Client_Server_Interface.Chat;

namespace ServerLib.Entities
{
    public delegate void ListOfNames(List<string> names, object sender);

    [ServiceContract(CallbackContract = typeof(IReceiveChatService))]
    public interface ISendChatService
    {
        [OperationContract(IsOneWay = true)]
        void SendMessage(string msg, string sender, string receiver);
        [OperationContract(IsOneWay = true)]
        void Start(string name);
        [OperationContract(IsOneWay = true)]
        void Stop(string name);

    }

    public interface IReceiveChatService
    {
        [OperationContract(IsOneWay = true)]
        void ReceiveMessage(string msg, string receiver);
        [OperationContract(IsOneWay = true)]
        void SendNames(List<string> names);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class ChatService : ISendChatService
    {

        readonly Dictionary<string, ClientInfo> _names = new Dictionary<string, ClientInfo>();

        public static event ListOfNames ChatListOfNames;

        IReceiveChatService _callback;

        public void Close()
        {
            lock (_names)
            {
                _callback = null;
                _names.Clear();
            }
        }

        public void Start(string name)
        {
            try
            {
                lock (_names)
                {
                    if (!_names.ContainsKey(name))
                    {
                        var ci = new ClientInfo
                        {
                            Proxy = OperationContext.Current.GetCallbackChannel<IReceiveChatService>(),
                            SessionId = OperationContext.Current.Channel.SessionId
                        };
                        //_callback = OperationContext.Current.GetCallbackChannel<IReceiveChatService>();
                        OperationContext.Current.Channel.Faulted += ChannelOnFaulted;
                        OperationContext.Current.Channel.Closed += ChannelOnFaulted;
                        AddUser(name, ci);
                        SendNamesToAll();
                        //StartChecking();
                    }
                    else
                    {

                        //_callback = OperationContext.Current.GetCallbackChannel<IReceiveChatService>();
                        //_callback.ReceiveMessage();
                        /*_names[name] = _callback;
                    _names[name].SendNames(_names.Keys.ToList());*/
                    }
                }
            }
            catch (Exception ex)
            {
                lock (_names)
                {
                    _names.Remove(name);
                }
                SendNamesToAll();
                return;
                //throw ex;
            }
        }

        private void ChannelOnFaulted(object sender, EventArgs eventArgs)
        {
            Disconnected((IContextChannel)sender);
        }

        void Disconnected(IContextChannel channel)
        {
            string sessionId = null;
            if (channel != null)
            {
                sessionId = channel.SessionId;
            }
            KeyValuePair<string, ClientInfo> client;
            try
            {
                lock (_names)
                {
                    client = _names.First(c => c.Value.SessionId == sessionId);
                }
            }
            catch (InvalidOperationException)
            {
                return;
            }
            lock (_names)
            {
                _names.Remove(client.Key);
            }
            SendNamesToAll();
        }

        public void Stop(string name)
        {
            lock (_names)
            {
                if (_names.ContainsKey(name))
                {
                    _names.Remove(name);
                    SendNamesToAll();
                    //if (_names.Count == 0)
                    //    StopChecking();
                }
            }
        }

        void SendNamesToAll()
        {
            var l = new List<string>();
            lock (_names)
            {
                foreach (var name in _names)
                {
                    IReceiveChatService proxy = name.Value.Proxy;
                    try
                    {
                        proxy.SendNames(_names.Keys.ToList());
                    }
                    catch (Exception)
                    {
                        l.Add(name.Key);
                    }
                }
            }
            foreach (var el in l)
            {
                lock (_names)
                {
                    if (_names.ContainsKey(el))
                    {
                        _names.Remove(el);
                    }
                }
            }

            if (ChatListOfNames != null)
                ChatListOfNames(_names.Keys.ToList(), this);
        }

        void ISendChatService.SendMessage(string msg, string sender, string receiver)
        {
            if (receiver == FunctionsParameters.GENERAL_MESSAGE)
            {
                lock (_names)
                {
                    foreach (var name in _names)
                    {
                        _callback = name.Value.Proxy;
                        _callback.ReceiveMessage(msg, sender);
                    }
                }
            }
            else
            {
                lock (_names)
                {
                    if (_names.ContainsKey(receiver))
                    {
                        _callback = _names[receiver].Proxy;
                        _callback.ReceiveMessage(msg, sender);
                    }
                    else
                    {
                        SendNamesToAll();
                    }
                }
            }
        }

        private void AddUser(string name, ClientInfo clientInfo)
        {
            lock (_names)
            {


                _names.Add(name, clientInfo);
                if (ChatListOfNames != null)
                    ChatListOfNames(_names.Keys.ToList(), this);
            }
        }

        class ClientInfo
        {
            public IReceiveChatService Proxy { get; set; }
            public string SessionId { get; set; }
        }

    }
}
